=begin pod

=TITLE class IO::Spec::Unix

=SUBTITLE Platform specific operations on file and directory paths for POSIX

    class IO::Spec::Unix is IO::Spec  { }

On object of this type is available via the variable C<$*SPEC> if the perl is
running on a Unix-like platform.

B<NOTE:> the C<IO::Spec::*> classes provide low-level path operations. Unless
you're creating your own high-level path manipulation routines, you don't
need to use C<IO::Spec::*>. Use L«C<IO::Path>|/type/IO::Path» instead.

B<NOTE2:> no special validation is done by these classes (e.g. check whether
path contains a null character). It is the job of higher-level classes, like
L«C<IO::Path>|/type/IO::Path», to do that.

=head1 Methods

=head2 method abs2rel

Defined as:

    method abs2rel(IO::Path:D $path, IO::Path:D $base = $*CWD --> Str:D)

Returns a string that represents C<$path>, but relative to C<$base> path.
Both C<$path> and C<$base> may be relative paths. C<$base> defaults to C<$*CWD>

=head2 method basename

Defined as:

    method basename(Str:D $path --> Str:D)

Takes a path as a string and returns a possibly-empty portion after the last
slash:

    IO::Spec::Unix.basename("foo/bar/") .perl.say; # OUTPUT: «""␤»
    IO::Spec::Unix.basename("foo/bar/.").perl.say; # OUTPUT: «"."␤»
    IO::Spec::Unix.basename("foo/bar")  .perl.say; # OUTPUT: «"bar"␤»

=head2 method canonpath

Defined as:

    method canonpath(Str() $path, :$parent --> Str:D)

Returns a string that is a canonical representation of C<$path>. If C<:$parent>
is set to true, will also clean up references to parent directories. B<NOTE:>
the routine does not access the filesystem, so no symlinks are followed.

    IO::Spec::Unix.canonpath("foo//../bar/../ber").say;
    # OUTPUT: «foo/../bar/../ber␤»

    IO::Spec::Unix.canonpath("foo///./../bar/../ber").say;
    # OUTPUT: «foo/../bar/../ber␤»

    IO::Spec::Unix.canonpath("foo///./../bar/../ber", :parent).say;
    # OUTPUT: «ber␤»

=head2 method catdir

Defined as:

    method catdir (*@parts --> Str:D)

Concatenates multiple path fragments and returns the canonical representation
of the resultant path as a string. The C<@parts> are L«C<Str>|/type/Str» objects
and are allowed to contain path separators.

    IO::Spec::Unix.catdir(<foo/bar ber perl>).say; # OUTPUT: «foo/bar/ber/perl␤»

=head2 method catfile

Alias for L«C<catdir>|/routine/catdir».

=head2 method catpath

Defined as:

    method catpath ($, Str:D $part1, Str:D $part2 --> Str:D)

Takes two path fragments and concatenates them, adding or removing a path
separator, if necessary. The first argument is ignored (it exists to maintain
consistent interface with other C<IO::Spec> types for systems that have
volumes).

    IO::Spec::Unix.catpath($, 'some/dir', 'and/more').say;
    # OUTPUT: «some/dir/and/more␤»

=head2 method curdir

Defined as:

    method curdir()

Returns a string representing the current directory:

    say '.' eq $*SPEC.curdir; # OUTPUT: «True␤»

=head2 method curupdir

Defined as:

    method curupdir()

Returns a L«C<none>|/routine/none» L<Junction> of strings representing the
current directory and the "one directory up":

=for code
my @dirs = <. foo .. bar>;
say @dirs.grep(* eq $*SPEC.curupdir); # OUTPUT: «(foo bar)␤»

=head2 method devnull

Defined as:

    method devnull(--> Str:D)

Returns the string C<"/dev/null"> representing the
L<"Null device"|https://en.wikipedia.org/wiki/Null_device>:

=for code
$*SPEC.devnull.IO.spurt: "foo bar baz";

=head2 method dir-sep

Defined as:

    method dir-sep(--> Str:D)

Returns the string C<"/"> representing canonical directory separator character.

=for code
IO::Spec::Unix.dir-sep.say; # OUTPUT: «/␤»

=head2 method extension

B<NOTE:> Most users would want to use the higher-level routine
L«C<IO::Path.extension>|/type/IO::Path#method_extension» instead of this
lower-level version.

Defined as:

    method extension(Str:D $path --> Str:D)

Takes a string representing a base name and returns the characters after
the last dot (C<".">), or empty string if no dots are present. The routine makes
no attempt to detect path separators and will return everything after the last
dot.

    $*SPEC.extension('foo.'      ).perl.say;  # OUTPUT: «""␤»
    $*SPEC.extension('foo.txt'   ).perl.say;  # OUTPUT: «"txt"␤»
    $*SPEC.extension('foo.tar.gz').perl.say;  # OUTPUT: «"gz"␤»
    $*SPEC.extension('foo'       ).perl.say;  # OUTPUT: «""␤»
    $*SPEC.extension('bar.foo/foo').perl.say; # OUTPUT: «"foo/foo"␤»

=head2 method is-absolute

Defined as:

    method is-absolute(Str:D $path --> Bool:D)

Returns C<True> if the C<$path> starts with a slash (C<"/">), even if it has
combining character on it:

    say IO::Spec::Unix.is-absolute: "/foo";        # OUTPUT: «True␤»
    say IO::Spec::Unix.is-absolute: "/\x[308]foo"; # OUTPUT: «True␤»
    say IO::Spec::Unix.is-absolute: "bar";         # OUTPUT: «False␤»

=head2 method join

=begin comment

  XXX TODO: I'm not exactly sure *why* we have .join along with .catpath and
  in what usecases join's *slightly* different behaviour is useful

=end comment

Defined as:

    method join ($, Str:D $dir, Str:D $file --> Str:D)

Similar to L«C<catpath>|/routine/catpath», takes two path fragments and
concatenates them, adding or removing a path separator, if necessary, except
it will return just C<$file> if both C<$dir> and C<$file> are string C<'/'>
or if C<$dir> is the string C<'.'>. The first argument is ignored (it exists to
maintain consistent interface with other C<IO::Spec> types for systems that have
volumes).

    IO::Spec::Unix.join($, 'foo', 'bar').say; # OUTPUT: «foo/bar␤»
    IO::Spec::Unix.join($, '/', '/').say;     # OUTPUT: «/␤»
    IO::Spec::Unix.join($, '.', 'foo').say;   # OUTPUT: «foo␤»

=head2 method path

Defined as:

    method path(--> Seq:D)

Splits the value of C«%*ENV<PATH>» on colons (C<":">), replaces empty parts with
C<".">, and returns a L<Seq> with each of the resultant parts. Returns
an empty L<Seq> if C«%*ENV<PATH>» is not set or is an empty string.

    %*ENV<PATH> = 'foo:bar/ber::foo:';
    IO::Spec::Unix.path.perl.say;
    # OUTPUT: «("foo", "bar/ber", ".", "foo", ".").Seq␤»

=head2 method rel2abs

Defined as:

    method rel2abs(Str() $path, $base = $*CWD --> Str:D)

Returns a string representing C<$path> converted to absolute path, based at
C<$base>, which defaults to C<$*CWD>. If C<$base> is not an absolute path,
it will be made absolute relative to C<$*CWD>, unless C<$*CWD> and C<$base>
are the same.

=begin code
say $*CWD;                                  # OUTPUT: «"/home/camelia".IO␤»

say IO::Spec::Unix.rel2abs: 'foo';          # OUTPUT: «/home/camelia/foo␤»
say IO::Spec::Unix.rel2abs: './';           # OUTPUT: «/home/camelia␤»
say IO::Spec::Unix.rel2abs: 'foo/../../';   # OUTPUT: «/home/camelia/foo/../..␤»
say IO::Spec::Unix.rel2abs: '/foo/';        # OUTPUT: «/foo␤»

say IO::Spec::Unix.rel2abs: 'foo', 'bar';   # OUTPUT: «/home/camelia/bar/foo␤»
say IO::Spec::Unix.rel2abs: './', '/bar';   # OUTPUT: «/bar␤»
say IO::Spec::Unix.rel2abs: '/foo/', 'bar'; # OUTPUT: «/foo␤»

say IO::Spec::Unix.rel2abs: 'foo/../../', 'bar';
# OUTPUT: «/home/camelia/bar/foo/../..␤»
=end code

=head2 method rootdir

Defined as:

    method rootdir(--> Str:D)

Returns string C<'/'>, representing root directory.

=head2 method split

Defined as:

    method split(Cool:D $path --> List:D)

Splits the given C<$path> into "volume", "dirname", and "basename" and
returns the result as a L<List> of three L<Pairs|/type/Pair>, in that order.
The "volume" is always an empty string and exists for consistency with other
L<IO::Spec> classes.

=begin code
IO::Spec::Unix.split('C:/foo/bar.txt').perl.say;
# OUTPUT: «(:volume(""), :dirname("C:/foo"), :basename("bar.txt"))␤»

IO::Spec::Unix.split('/foo/').perl.say;
# OUTPUT: «(:volume(""), :dirname("/"), :basename("foo"))␤»

IO::Spec::Unix.split('///').perl.say;
# OUTPUT: «(:volume(""), :dirname("/"), :basename("/"))␤»

IO::Spec::Unix.split('./').perl.say;
# OUTPUT: «(:volume(""), :dirname("."), :basename("."))␤»

IO::Spec::Unix.split('.').perl.say;
# OUTPUT: «(:volume(""), :dirname("."), :basename("."))␤»

IO::Spec::Unix.split('').perl.say;
# OUTPUT: «(:volume(""), :dirname(""), :basename(""))␤»
=end code

=head2 method splitdir

Defined as:

    method splitdir(Cool:D $path --> List:D)

Splits the given C<$path> on slashes.

=begin code
IO::Spec::Unix.splitdir('C:\foo/bar.txt').perl.say;
# OUTPUT: «("C:\\foo", "bar.txt")␤»

IO::Spec::Unix.splitdir('/foo/').perl.say;
# OUTPUT: «("", "foo", "")␤»

IO::Spec::Unix.splitdir('///').perl.say;
# OUTPUT: «("", "", "", "")␤»

IO::Spec::Unix.splitdir('./').perl.say;
# OUTPUT: «(".", "")␤»

IO::Spec::Unix.splitdir('.').perl.say;
# OUTPUT: «(".",)␤»

IO::Spec::Unix.splitdir('').perl.say;
# OUTPUT: «("",)␤»
=end code

=head2 method splitpath

Defined as:

    method splitpath(Cool:D $path, :$nofile --> List:D)

Splits the given C<$path> into a list of 3 strings: volume,
dirname, and file. The volume is always an empty string, returned for API
compatibility with other L<IO::Spec> types. If C<:$nofile> named argument is
set to C<True>, the content of the file string is undefined and should be
ignored; this is a means to get a performance boost, as implementations may use
faster code path when file is not needed.

=begin code
IO::Spec::Unix.splitpath('C:\foo/bar.txt').perl.say;
# OUTPUT: «("", "C:\\foo/", "bar.txt")␤»

IO::Spec::Unix.splitpath('C:\foo/bar.txt', :nofile).perl.say;
# OUTPUT: «("", "C:\\foo/bar.txt", "")␤»

IO::Spec::Unix.splitpath('/foo/').perl.say;
# OUTPUT: «("", "/foo/", "")␤»

IO::Spec::Unix.splitpath('/foo/', :nofile).perl.say;
# OUTPUT: «("", "/foo/", "")␤»

IO::Spec::Unix.splitpath('///').perl.say;
# OUTPUT: «("", "///", "")␤»

IO::Spec::Unix.splitpath('./').perl.say;
# OUTPUT: «("", "./", "")␤»

IO::Spec::Unix.splitpath('.').perl.say;
# OUTPUT: «("", "", ".")␤»

IO::Spec::Unix.splitpath('').perl.say;
# OUTPUT: «("", "", "")␤»
=end code

=head2 method tmpdir

Defined as:

        method tmpdir(--> IO::Path:D)

Attempts to locate a system's temporary directory by checking several typical directories and environmental variables. Uses current directory if no suitable directories are found.

=head2 method updir

Defined as:

    method updir()

Returns a string representing the directory one up from current:

    say '..' eq $*SPEC.updir; # OUTPUT: «True␤»

=end pod

# vim: expandtab shiftwidth=4 ft=perl6
